import 'package:flutter/cupertino.dart';
import 'package:logger/logger.dart';

import '../../dao/ImMessageDao.dart';
import '../../dao/ImSessionDao.dart';
import '../../pojo/ImPojo.dart';
import '../../service/ImMessageService.dart';
import '../../socket/dto/business/SendData.dart';
import '../../socket/service/RpcSendDataService.dart';
import '../../util/DateUtil.dart';
import '../../util/ImConstant.dart';
import '../../util/ImSingleton.dart';
import '../view/model/DataChangeListener.dart';

class MessageListModel {

  // final messageDao = ImMessageDao();

  final int sessionId;
  final DataChangeStateListener<StatefulWidget, ImMessage> messageDataChangeListener;

  MessageListModel({required this.sessionId, required this.messageDataChangeListener});

  final _logger = Logger(
    printer: PrettyPrinter(),
  );

  Future<List<ImMessage>> loadData(int sessionId, ImMessage message) async {
    var messages = await _loadData(sessionId, message, 100);
    return messages;
  }

  Future<List<ImMessage>> _loadData(int sessionId, ImMessage message, int pageSize) async {
    final messageDao = ImMessageDao();
    var messages = await messageDao.getNewsBySessionId(sessionId, message.serverReceiveTime, pageSize);
    if (messages.length == 0) return [];
    int idx = 0;
    for (var dbMessage in messages) {
      if (dbMessage.id == message.id) {
        break;
      }
      idx++;
    }
    if (idx == messages.length) {
      return _loadData(sessionId, message, pageSize + 100);
    }
    return messages.sublist(idx);
  }

  Future<List<ImMessage>> loadNewsData() async {
    var messageDao = ImMessageDao();
    var startDate = DateTime.now().add(const Duration(days: -ImConstant.IM_SESSION_SHOW_DAYS));
    if (messageDataChangeListener.dataList.isNotEmpty) {
      var message = messageDataChangeListener.dataList[0];
      startDate = message.serverReceiveTime??(message.createTime!);
    }
    _logger.i("$sessionId begin load news data! statTime: ${DateUtil.formatDate(startDate)}");
    List<ImMessage> result = await messageDao.getNewsBySessionId(sessionId, startDate, ImConstant.MESSAGE_LIST_NEXT_SIZE);
    await Future.delayed(Duration(milliseconds: 2000));
    result = _removeRepeat(result, messageDataChangeListener.dataList);

    if(result.isNotEmpty) {
      List<ImMessage> messages = _mergeMessages(result, messageDataChangeListener.dataList);
      _sortImMessages(messages);        // 排序
      messageDataChangeListener.setData(messages);
      _sendReadMessageAndReduceHotMessageTotal(messages);
    }
    return result;
  }

  Future<List<ImMessage>> loadOldData() async {
    _logger.i("$sessionId begin load old data!");
    var messageService = ImMessageService();
    var startDate = DateTime.now().add(const Duration(days: -ImConstant.IM_SESSION_SHOW_DAYS));
    if (messageDataChangeListener.dataList.isNotEmpty) {
      var message = messageDataChangeListener.dataList[messageDataChangeListener.dataList.length - 1];
      startDate = message.serverReceiveTime??(message.createTime!);
    }
    _logger.i("$sessionId begin load old data! statTime: ${DateUtil.formatDate(startDate)}");
    List<ImMessage> result = await messageService.getOldBySessionId(sessionId, startDate, ImConstant.MESSAGE_LIST_NEXT_SIZE);
    await Future.delayed(Duration(milliseconds: 2000));
    result = _removeRepeat(result, messageDataChangeListener.dataList);
    if(result.isNotEmpty) {
      List<ImMessage> messages = _mergeMessages(result, messageDataChangeListener.dataList.reversed.toList());
      _sortImMessages(messages);        // 排序
      messageDataChangeListener.setData(messages);
      _sendReadMessageAndReduceHotMessageTotal(messages);
    }
    return result;
  }

  Future<void> _sendReadMessageAndReduceHotMessageTotal(List<ImMessage> messages) async {
    await _sendReadMessages(messages);
    await _reduceHotMessageTotal();// 发送未读消息事件
  }

  Future<void> _sendReadMessages(List<ImMessage> messages) async {
    RpcMessageService rpcMessageService = new RpcMessageService();
    String userId = ImSingleton.getInstance().session.userId!;
    List<ImMessage> sendReadMessages = _filterNeedHandleMessages(userId, messages);
    if (sendReadMessages.isNotEmpty) {
      List<String> serverIds = sendReadMessages.where((m) => m.serverId != null).map((m) => m.serverId!).toList();
      var messageReadReq = new MessageReadReq(messageIds: serverIds);
      await rpcMessageService.sendReadMessages(messageReadReq);
      sendReadMessages.forEach((m) {
        m.actionStatus = ImMessageActionStatus.readed;
        m.updateTime = DateTime.now();
      });
      var messageDao = ImMessageDao();
      await messageDao.batchChangeMessageStatus(sendReadMessages);
    }
  }

  Future<void> _reduceHotMessageTotal() async {
    var sessionDao = ImSessionDao();
    sessionDao.cleanHotMessageNum(sessionId);
    return Future.value(null);

  }

  List<ImMessage> _filterNeedHandleMessages(String userId, List<ImMessage> messages) {
    List<ImMessage> needHandleMessages = [];    // 改变状态的为已读的消息
    for (ImMessage message in messages) {
      if (message.isSender(userId)) continue;
      if (message.actionStatus != ImMessageActionStatus.none && message.actionStatus != null && message.actionStatus
          != ImMessageActionStatus.read) continue;
      needHandleMessages.add(message);
    }
    return needHandleMessages;
  }

  List<ImMessage> _removeRepeat(List<ImMessage> messages, List<ImMessage> messages2) {
    List<ImMessage> result = [];
    for (var message in messages) {
      if (!messages2.contains(message)) {
        result.add(message);
      }
    }
    return result;
  }

  List<ImMessage> _mergeMessages(List<ImMessage> messages, List<ImMessage> messages2) {
    List<ImMessage> result = List.of(messages);
    for (var message in messages2) {
      if (result.length >= ImConstant.MESSAGE_LIST_MAX_SIZE) {
        break;
      } else {
        result.add(message);
      }
    }
    return result;
  }

  void _sortImMessages(List<ImMessage> messages) {
    if (messages.isEmpty) return;
    messages.sort((a, b) {
      final DateTime? s1 = a.serverReceiveTime;
      final DateTime? s2 = b.serverReceiveTime;
      if (s1 == null && s2 == null) return 0;
      if (s1 == null && s2 != null) return 1;
      if (s1 != null && s2 == null) return -1;
      if (s1 != null && s2 != null) {
        return s2.compareTo(s1);
      }
      return 0;
    });
  }
}